Skip to content

test: resolve challenge records through the mock, and check that it happened - #1144

Open
kvinwang wants to merge 1 commit into
feat/certbot-dns-persist-01from
feat/e2e-resolve-challenge-records
Open

test: resolve challenge records through the mock, and check that it happened#1144
kvinwang wants to merge 1 commit into
feat/certbot-dns-persist-01from
feat/e2e-resolve-challenge-records

Conversation

@kvinwang

Copy link
Copy Markdown
Collaborator

Problem

certbot resolves each challenge name itself before telling the CA to go and
look, so a record that has not propagated is reported by name instead of as an
order failure. The E2E suite never exercised it.

The gateway resolved through Docker's embedded DNS, which has never heard of
the test zones. So every self-check ran its full budget, warned, and proceeded
— for dns-01 too, seconds after certbot itself had written the record
through the mock's Cloudflare API:

WARN  no authoritative nameserver for _acme-challenge.test0.local,
      using the system resolver
DEBUG challenge not found, waiting for 500ms  tries=2  max_wait=30s
DEBUG challenge not found, waiting for 1s     tries=3

Only Pebble read the mock's DNS, via -dnsserver. Everything the client does
with DNS — the authoritative-nameserver discovery, the retry loop, the advisory
timeout — was covered in its failure path only, and every order paid the full
wait for nothing.

Fix

The mock forwards what it does not own. A name outside its zones was
answered NOERROR with no records, which a resolver reads as an authoritative
"no such record" and does not retry elsewhere — so a client pointed here as its
only resolver would lose the other containers. Those questions now go upstream,
which is what makes it a resolver rather than only a CA's -dnsserver.

The gateways use it. One dns: entry per gateway service.

The mock says what it was asked. The check is advisory, so an order
succeeds whether or not it worked and nothing downstream reveals which. Every
question is logged and served at /api/dns-queries.

Cases

  • Answered — for a record the suite published. Observed, not triggered: the
    periodic renewal picks a domain up as soon as it is added, so forcing one
    races it and can find nothing left to do.
  • Asked repeatedly, never answered — for a name with no record. Polling is
    the point, and giving up must not stop the order.

Each keys off a name nothing else asks for, so they need no clearing and do not
depend on ordering.

Verification

Full suite, this branch vs its base, same host:

passed failed
base (feat/certbot-dns-persist-01) 16 22
this branch 18 22

The two new passes are the two new cases; the 22 failures are identical and
pre-existing on this host, all in the proxy/TLS phases — the container resolver
inherits the host's search domain, so _dstack-app-address-wildcard.<name> is
looked up under it, and RA-TLS peering reports peer certificate does not contain app_id. Neither is touched here.

A self-check that resolves now settles in seconds instead of spending the whole
advisory wait, so the suite gets that time back on every order.

Base

Stacked on #1132, which is where the DNS-capable mock and the gateway suite's
use of it come from. Merge that first.

Copilot AI lite review requested due to automatic review settings August 26, 2026 07:09

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

…appened

certbot resolves each challenge name itself before telling the CA to look, so
that a record which has not propagated is reported by name rather than as an
order failure. Nothing in the suite exercised that: the gateway resolved
through Docker's embedded DNS, which has never heard of the test zones, so
every self-check ran its full budget, warned, and proceeded -- for dns-01 too,
seconds after certbot itself wrote the record.

    WARN no authoritative nameserver for _acme-challenge.test0.local,
         using the system resolver
    DEBUG challenge not found, waiting for 500ms tries=2 max_wait=30s

Pointing the gateways at the mock needs the mock to stop being a black hole for
everything else: a name it does not know was answered NOERROR with no records,
which a resolver reads as authoritative and does not retry elsewhere, so the
gateway would lose the other containers. It now forwards those upstream, which
is what makes it usable as a client's only resolver rather than only as a CA's
`-dnsserver`.

The check is advisory, so nothing downstream reveals whether it worked -- an
order succeeds either way. The mock therefore logs every question it is asked
and serves them at /api/dns-queries, and the two cases are asserted directly:

- Answered, for a record the suite published. Observed rather than triggered:
  the periodic renewal picks a domain up as soon as it is added, so forcing one
  races it and can find nothing left to do.
- Asked repeatedly and never answered, for a name with no record. Polling is
  the point -- a record may still be propagating -- and giving up must not stop
  the order.

Each keys off a name nothing else asks for, so they need no clearing and do not
depend on running in any order.

Incidentally the suite gets its time back: a self-check that resolves settles
in seconds instead of spending the whole advisory wait on every order.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants